 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.ui.IActionFilter;
 import org.eclipse.ui.internal.util.BundleUtility;
 import org.eclipse.ui.internal.util.Util;
 import org.osgi.framework.Bundle;

 /**
  * An ActionExpression is used to evaluate the enablement / visibility criteria
  * for an action.
  */
 public class ActionExpression {

     private static abstract class AbstractExpression {

         /**
          * The hash code for this object. This value is computed lazily, and
          * marked as invalid when one of the values on which it is based
          * changes.
          */
         protected transient int expressionHashCode = HASH_CODE_NOT_COMPUTED;

         /**
          * Extract the object class tests from the expression. This allows
          * clients (e.g. the decorator manager) to handle object class testing
          * in a more optimized way. This method extracts the objectClass test
          * from the expression and returns the object classes. The expression is
          * not changed and a <code>null</code> is returned if no object class
          * is found.
          *
          * @return String[] the object class names or <code>null</code> if
          * none was found.
          */
         public String [] extractObjectClasses() {
             return null;
         }

         /**
          * Returns whether the expression is valid for the given object.
          *
          * @param object
          * the object to validate against (can be <code>null</code>)
          * @return boolean whether the expression is valid for the object.
          */
         public abstract boolean isEnabledFor(Object object);

         /**
          * Returns whether or not the receiver is potentially valid for the
          * object via just the extension type. Currently the only supported
          * expression type is <code>EXP_TYPE_OBJECT_CLASS</code>.
          *
          * @param object
          * the object to validate against (can be <code>null</code>)
          * @param expressionType
          * the expression type to consider
          * @return boolean whether the expression is potentially valid for the
          * object.
          */
         public boolean isEnabledForExpression(Object object,
                 String expressionType) {
             return false;
         }

         /**
          * Return the value of the expression type that the receiver is enabled
          * for. If the receiver is not enabled for the expressionType then
          * return <code>null</code>.
          *
          * @param expressionType
          * the expression type to consider
          * @return Collection of String if there are values for this expression
          * or <code>null</code> if this is not possible in the
          * receiver or any of it's children
          */
         public Collection valuesForExpression(String expressionType) {
             return null;
         }
     }

     private static class AndExpression extends CompositeExpression {

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for And.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public AndExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super(element);
         }

         public final boolean equals(final Object object) {
             if (object instanceof AndExpression) {
                 final AndExpression that = (AndExpression) object;
                 return Util.equals(this.list, that.list);
             }

             return false;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             Iterator iter = list.iterator();
             while (iter.hasNext()) {
                 AbstractExpression expr = (AbstractExpression) iter.next();
                 if (!expr.isEnabledFor(object)) {
                     return false;
                 }
             }
             return true;
         }
     }

     private static abstract class CompositeExpression extends
             AbstractExpression {
         /**
          *
          */
         protected ArrayList list;

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The composite element we will create the expression from.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public CompositeExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             IConfigurationElement[] children = element.getChildren();
             if (children.length == 0) {
                 throw new IllegalStateException (
                         "Composite expression cannot be empty"); //$NON-NLS-1$
 }

             list = new ArrayList (children.length);
             for (int i = 0; i < children.length; i++) {
                 String tag = children[i].getName();
                 AbstractExpression expr = createExpression(children[i]);
                 if (EXP_TYPE_OBJECT_CLASS.equals(tag)) {
                     list.add(0, expr);
                 } else {
                     list.add(expr);
                 }
             }
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#extractObjectClasses()
          */
         public String [] extractObjectClasses() {
             Iterator iterator = list.iterator();
             List classNames = null;
             while (iterator.hasNext()) {
                 AbstractExpression next = (AbstractExpression) iterator.next();
                 String [] objectClasses = next.extractObjectClasses();
                 if (objectClasses != null) {
                     if (classNames == null) {
                         classNames = new ArrayList ();
                     }
                     for (int i = 0; i < objectClasses.length; i++) {
                         classNames.add(objectClasses[i]);
                     }
                 }
             }
             if (classNames == null) {
                 return null;
             }

             String [] returnValue = new String [classNames.size()];
             classNames.toArray(returnValue);
             return returnValue;
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(list);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledForExpression(Object object,
                 String expressionType) {
             Iterator iterator = list.iterator();
             while (iterator.hasNext()) {
                 AbstractExpression next = (AbstractExpression) iterator.next();
                 if (next.isEnabledForExpression(object, expressionType)) {
                     return true;
                 }
             }
             return false;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#valuesForExpression(java.lang.String)
          */
         public Collection valuesForExpression(String expressionType) {
             Iterator iterator = list.iterator();
             Collection allValues = null;
             while (iterator.hasNext()) {
                 AbstractExpression next = (AbstractExpression) iterator.next();
                 Collection values = next.valuesForExpression(expressionType);
                 if (values != null) {
                     if (allValues == null) {
                         allValues = values;
                     } else {
                         allValues.addAll(values);
                     }
                 }

             }
             return allValues;
         }
     }

     private static class NotExpression extends SingleExpression {

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to create the definition for
          * the receiver.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public NotExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super(element);
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             return !super.isEnabledFor(object);
         }
     }

     private static class ObjectClassExpression extends AbstractExpression {
         private String className;

         private boolean extracted;

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for objectClass.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public ObjectClassExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             className = element.getAttribute(ATT_NAME);
             if (className == null) {
                 throw new IllegalStateException (
                         "Object class expression missing name attribute"); //$NON-NLS-1$
 }
         }

         /**
          * Create an ObjectClass expression based on the className. Added for
          * backwards compatibility.
          *
          * @param className
          */
         public ObjectClassExpression(String className) {
             super();

             if (className != null) {
                 this.className = className;
             } else {
                 throw new IllegalStateException (
                         "Object class expression must have class name"); //$NON-NLS-1$
 }
         }

         /**
          * Check the interfaces the whole way up. If one of them matches
          * className return <code>true</code>.
          *
          * @param interfaceToCheck
          * The interface whose name we are testing against.
          * @return <code>true</code> if one of the interfaces in the hierarchy
          * matches className, <code>false</code> otherwise.
          */
         private boolean checkInterfaceHierarchy(Class interfaceToCheck) {
             if (interfaceToCheck.getName().equals(className)) {
                 return true;
             }
             Class [] superInterfaces = interfaceToCheck.getInterfaces();
             for (int i = 0; i < superInterfaces.length; i++) {
                 if (checkInterfaceHierarchy(superInterfaces[i])) {
                     return true;
                 }
             }
             return false;
         }

         public final boolean equals(final Object object) {
             if (object instanceof ObjectClassExpression) {
                 final ObjectClassExpression that = (ObjectClassExpression) object;
                 return Util.equals(this.className, that.className)
                         && Util.equals(this.extracted, that.extracted);
             }

             return false;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#extractObjectClasses()
          */
         public String [] extractObjectClasses() {
             extracted = true;
             return new String [] { className };
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR
                         + Util.hashCode(className);
                 expressionHashCode = expressionHashCode * HASH_FACTOR + Util.hashCode(extracted);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             if (object == null) {
                 return false;
             }
             if (extracted) {
                 return true;
             }

             Class clazz = object.getClass();
             while (clazz != null) {
                 // test the class itself
 if (clazz.getName().equals(className)) {
                     return true;
                 }

                 // test all the interfaces the class implements
 Class [] interfaces = clazz.getInterfaces();
                 for (int i = 0; i < interfaces.length; i++) {
                     if (checkInterfaceHierarchy(interfaces[i])) {
                         return true;
                     }
                 }

                 // get the superclass
 clazz = clazz.getSuperclass();
             }

             return false;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledForExpression(Object object,
                 String expressionType) {
             if (expressionType.equals(EXP_TYPE_OBJECT_CLASS)) {
                 return isEnabledFor(object);
             }
             return false;
         }
     }

     private static class ObjectStateExpression extends AbstractExpression {
         private String name;

         private String value;

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for objectState.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public ObjectStateExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             name = element.getAttribute(ATT_NAME);
             value = element.getAttribute(ATT_VALUE);
             if (name == null || value == null) {
                 throw new IllegalStateException (
                         "Object state expression missing attribute"); //$NON-NLS-1$
 }
         }

         public final boolean equals(final Object object) {
             if (object instanceof ObjectStateExpression) {
                 final ObjectStateExpression that = (ObjectStateExpression) object;
                 return Util.equals(this.name, that.name)
                         && Util.equals(this.value, that.value);
             }

             return false;
         }

         private IActionFilter getActionFilter(Object object) {
             return (IActionFilter)Util.getAdapter(object, IActionFilter.class);
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(name);
                 expressionHashCode = expressionHashCode * HASH_FACTOR + Util.hashCode(value);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             if (object == null) {
                 return false;
             }

             // Try out the object first.
 if (preciselyMatches(object)) {
                 return true;
             }

             // Try out the underlying resource.
 Class resourceClass = LegacyResourceSupport.getResourceClass();
             if (resourceClass == null) {
                 return false;
             }

             if (resourceClass.isInstance(object)) {
                 return false;
             }

             Object res = Util.getAdapter(object, resourceClass);
             if (res == null) {
                 return false;
             }

             return preciselyMatches(res);

         }

         private boolean preciselyMatches(Object object) {
             // Get the action filter.
 IActionFilter filter = getActionFilter(object);
             if (filter == null) {
                 return false;
             }

             // Run the action filter.
 return filter.testAttribute(object, name, value);
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#valuesForExpression(java.lang.String)
          */
         public Collection valuesForExpression(String expressionType) {
             if (expressionType.equals(name)) {
                 Collection returnValue = new HashSet ();
                 returnValue.add(value);
                 return returnValue;
             }
             return null;
         }

     }

     private static class OrExpression extends CompositeExpression {

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for Or.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public OrExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super(element);
         }

         public final boolean equals(final Object object) {
             if (object instanceof OrExpression) {
                 final OrExpression that = (OrExpression) object;
                 return Util.equals(this.list, that.list);
             }

             return false;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             Iterator iter = list.iterator();
             while (iter.hasNext()) {
                 AbstractExpression expr = (AbstractExpression) iter.next();
                 if (expr.isEnabledFor(object)) {
                     return true;
                 }
             }
             return false;
         }
     }

     private static class PluginStateExpression extends AbstractExpression {
         private String id;

         private String value;

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for pluginState.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public PluginStateExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             id = element.getAttribute(ATT_ID);
             value = element.getAttribute(ATT_VALUE);
             if (id == null || value == null) {
                 throw new IllegalStateException (
                         "Plugin state expression missing attribute"); //$NON-NLS-1$
 }
         }

         public final boolean equals(final Object object) {
             if (object instanceof PluginStateExpression) {
                 final PluginStateExpression that = (PluginStateExpression) object;
                 return Util.equals(this.id, that.id)
                         && Util.equals(this.value, that.value);
             }

             return false;
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(id);
                 expressionHashCode = expressionHashCode * HASH_FACTOR + Util.hashCode(value);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             Bundle bundle = Platform.getBundle(id);
             if (!BundleUtility.isReady(bundle)) {
                 return false;
             }
             if (value.equals(PLUGIN_INSTALLED)) {
                 return true;
             }
             if (value.equals(PLUGIN_ACTIVATED)) {
                 return BundleUtility.isActivated(bundle);
             }
             return false;
         }
     }

     private static class SingleExpression extends AbstractExpression {
         private AbstractExpression child;

         /**
          * Create a single expression from the abstract definition.
          *
          * @param expression
          * The expression that will be the child of the new single
          * expression.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public SingleExpression(AbstractExpression expression)
                 throws IllegalStateException {
             super();

             if (expression != null) {
                 child = expression;
             } else {
                 throw new IllegalStateException (
                         "Single expression must contain 1 expression"); //$NON-NLS-1$
 }
         }

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element to create the expression from.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public SingleExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             IConfigurationElement[] children = element.getChildren();
             if (children.length != 1) {
                 throw new IllegalStateException (
                         "Single expression does not contain only 1 expression"); //$NON-NLS-1$
 }
             child = createExpression(children[0]);
         }

         public final boolean equals(final Object object) {
             if (object instanceof SingleExpression) {
                 final SingleExpression that = (SingleExpression) object;
                 return Util.equals(this.child, that.child);
             }

             return false;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#extractObjectClasses()
          */
         public String [] extractObjectClasses() {
             return child.extractObjectClasses();
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(child);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             return child.isEnabledFor(object);
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledForExpression(Object object,
                 String expressionType) {
             return child.isEnabledForExpression(object, expressionType);
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.ActionExpression.AbstractExpression#valuesForExpression(java.lang.String)
          */
         public Collection valuesForExpression(String expressionType) {
             return child.valuesForExpression(expressionType);
         }

     }

     private static class SystemPropertyExpression extends AbstractExpression {
         private String name;

         private String value;

         /**
          * Creates and populates the expression from the attributes and sub-
          * elements of the configuration element.
          *
          * @param element
          * The element that will be used to determine the expressions
          * for systemProperty.
          * @throws IllegalStateException
          * if the expression tag is not defined in the schema.
          */
         public SystemPropertyExpression(IConfigurationElement element)
                 throws IllegalStateException {
             super();

             name = element.getAttribute(ATT_NAME);
             value = element.getAttribute(ATT_VALUE);
             if (name == null || value == null) {
                 throw new IllegalStateException (
                         "System property expression missing attribute"); //$NON-NLS-1$
 }
         }

         /*
          * (non-Javadoc) Method declared on AbstractExpression.
          */
         public boolean isEnabledFor(Object object) {
             String str = System.getProperty(name);
             if (str == null) {
                 return false;
             }
             return value.equals(str);
         }

         public final boolean equals(final Object object) {
             if (object instanceof SystemPropertyExpression) {
                 final SystemPropertyExpression that = (SystemPropertyExpression) object;
                 return Util.equals(this.name, that.name)
                         && Util.equals(this.value, that.value);
             }

             return false;
         }

         /**
          * Computes the hash code for this object based on the id.
          *
          * @return The hash code for this object.
          */
         public final int hashCode() {
             if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                 expressionHashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(name);
                 expressionHashCode = expressionHashCode * HASH_FACTOR + Util.hashCode(value);
                 if (expressionHashCode == HASH_CODE_NOT_COMPUTED) {
                     expressionHashCode++;
                 }
             }
             return expressionHashCode;
         }
     }

     private static final String ATT_ID = "id"; //$NON-NLS-1$

     private static final String ATT_NAME = "name"; //$NON-NLS-1$

     private static final String ATT_VALUE = "value"; //$NON-NLS-1$

     /**
      * Constant definition for AND.
      *
      */
     public static final String EXP_TYPE_AND = "and"; //$NON-NLS-1$

     /**
      * Constant definition for NOT.
      *
      */
     public static final String EXP_TYPE_NOT = "not"; //$NON-NLS-1$

     /**
      * Constant definition for objectClass.
      *
      */
     public static final String EXP_TYPE_OBJECT_CLASS = "objectClass"; //$NON-NLS-1$

     /**
      * Constant definition for objectState.
      *
      */
     public static final String EXP_TYPE_OBJECT_STATE = "objectState"; //$NON-NLS-1$

     /**
      * Constant definition for OR.
      *
      */
     public static final String EXP_TYPE_OR = "or"; //$NON-NLS-1$

     /**
      * Constant definition for pluginState.
      *
      */
     public static final String EXP_TYPE_PLUG_IN_STATE = "pluginState"; //$NON-NLS-1$

     /**
      * Constant definition for systemProperty.
      *
      */
     public static final String EXP_TYPE_SYSTEM_PROPERTY = "systemProperty"; //$NON-NLS-1$

     /**
      * The constant integer hash code value meaning the hash code has not yet
      * been computed.
      */
     private static final int HASH_CODE_NOT_COMPUTED = -1;

     /**
      * A factor for computing the hash code for all schemes.
      */
     private static final int HASH_FACTOR = 89;

     /**
      * The seed for the hash code for all schemes.
      */
     private static final int HASH_INITIAL = ActionExpression.class.getName()
             .hashCode();

     private static final String PLUGIN_ACTIVATED = "activated"; //$NON-NLS-1$

     private static final String PLUGIN_INSTALLED = "installed"; //$NON-NLS-1$

     /**
      * Create an expression from the attributes and sub-elements of the
      * configuration element.
      *
      * @param element
      * The IConfigurationElement with a tag defined in the public
      * constants.
      * @return AbstractExpression based on the definition
      * @throws IllegalStateException
      * if the expression tag is not defined in the schema.
      */
     private static AbstractExpression createExpression(
             IConfigurationElement element) throws IllegalStateException {
         String tag = element.getName();
         if (tag.equals(EXP_TYPE_OR)) {
             return new OrExpression(element);
         }
         if (tag.equals(EXP_TYPE_AND)) {
             return new AndExpression(element);
         }
         if (tag.equals(EXP_TYPE_NOT)) {
             return new NotExpression(element);
         }
         if (tag.equals(EXP_TYPE_OBJECT_STATE)) {
             return new ObjectStateExpression(element);
         }
         if (tag.equals(EXP_TYPE_OBJECT_CLASS)) {
             return new ObjectClassExpression(element);
         }
         if (tag.equals(EXP_TYPE_PLUG_IN_STATE)) {
             return new PluginStateExpression(element);
         }
         if (tag.equals(EXP_TYPE_SYSTEM_PROPERTY)) {
             return new SystemPropertyExpression(element);
         }

         throw new IllegalStateException (
                 "Action expression unrecognized element: " + tag); //$NON-NLS-1$
 }

     /**
      * The hash code for this object. This value is computed lazily, and marked
      * as invalid when one of the values on which it is based changes.
      */
     private transient int hashCode = HASH_CODE_NOT_COMPUTED;

     private SingleExpression root;

     /**
      * Creates an action expression for the given configuration element.
      *
      * @param element
      * The element to build the expression from.
      */
     public ActionExpression(IConfigurationElement element) {
         try {
             root = new SingleExpression(element);
         } catch (IllegalStateException e) {
             e.printStackTrace();
             root = null;
         }
     }

     /**
      * Create an instance of the receiver with the given expression type and
      * value. Currently the only supported expression type is
      * <code>EXP_TYPE_OBJECT_CLASS</code>.
      *
      * @param expressionType
      * The expression constant we are creating an instance of.
      * @param expressionValue
      * The name of the class we are creating an expression for.
      */
     public ActionExpression(String expressionType, String expressionValue) {
         if (expressionType.equals(EXP_TYPE_OBJECT_CLASS)) {
             root = new SingleExpression(new ObjectClassExpression(
                     expressionValue));
         }
     }

     public final boolean equals(final Object object) {
         if (object instanceof ActionExpression) {
             final ActionExpression that = (ActionExpression) object;
             return Util.equals(this.root, that.root);
         }

         return false;
     }

     /**
      * Extract the object class test from the expression. This allows clients
      * (e.g. the decorator manager) to handle object class testing in a more
      * optimized way. This method removes the objectClass test from the
      * expression and returns the object class. The expression is not changed
      * and a <code>null</code> is returned if no object class is found.
      *
      * @return the object class or <code>null</code> if none was found.
      */
     public String [] extractObjectClasses() {
         return root.extractObjectClasses();
     }

    /**
     * Computes the hash code for this object based on the id.
     *
     * @return The hash code for this object.
     */
    public final int hashCode() {
        if (hashCode == HASH_CODE_NOT_COMPUTED) {
            hashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(root);
            if (hashCode == HASH_CODE_NOT_COMPUTED) {
                hashCode++;
            }
        }
        return hashCode;
    }

    /**
     * Returns whether the expression is valid for all elements of the given
     * selection.
     *
     * @param selection
     * the structured selection to use
     * @return boolean whether the expression is valid for the selection.
     */
    public boolean isEnabledFor(IStructuredSelection selection) {
        if (root == null) {
            return false;
        }

        if (selection == null || selection.isEmpty()) {
            return root.isEnabledFor(null);
        }

        Iterator elements = selection.iterator();
        while (elements.hasNext()) {
            if (!isEnabledFor(elements.next())) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns whether the expression is valid for the given object.
     *
     * @param object
     * the object to validate against (can be <code>null</code>)
     * @return boolean whether the expression is valid for the object.
     */
    public boolean isEnabledFor(Object object) {
        if (root == null) {
            return false;
        }
        return root.isEnabledFor(object);
    }

    /**
     * Returns whether or not the receiver is potentially valid for the object
     * via just the extension type. Currently the only supported expression type
     * is <code>EXP_TYPE_OBJECT_CLASS</code>.
     *
     * @param object
     * the object to validate against (can be <code>null</code>)
     * @param expressionType
     * the expression type to consider
     * @return boolean whether the expression is potentially valid for the
     * object.
     */
    public boolean isEnabledForExpression(Object object, String expressionType) {
        if (root == null) {
            return false;
        }
        return root.isEnabledForExpression(object, expressionType);
    }

    /**
     * Return the values of the expression type that the receiver is enabled
     * for. If the receiver is not enabled for the expressionType then return
     * <code>null</code>.
     *
     * @param expressionType
     * the expression type to consider
     * @return Collection if there are values for this expression or
     * <code>null</code> if this is not possible in the receiver or
     * any of it's children
     */
    public Collection valuesForExpression(String expressionType) {
        return root.valuesForExpression(expressionType);
    }
}

